package com.livestocksolutions.util;

import java.util.Date;
import java.util.HashMap;
import java.util.Set;

/* -- Version History --
 *  1.0 : 22 September 2012 - Initial version.
 */

/**
 * Record the date and the changes that have occurred.
 * @author Adam Parr
 * @version 1.0
 * 
 * @.modified 22 September 2012
 */
public class UpdateHistory{
	/**
	 * The date of this modification as a long value.
	 */
	protected long date;
	/**
	 * The new value of the changed
	 */
	protected String value;
	/**
	 * The Value of an innerObject that has been modified.
	 */
	protected HashMap<String,UpdateHistory> innerObject = new HashMap<String,UpdateHistory>();
	
	/**
	 * Constructor.
	 */
	public UpdateHistory(){
	}
	
	/**
	 * Constructor to set a value only.
	 * @param value The String value to set.
	 */
	public UpdateHistory(String value){
		this.value=value;
	}
	
	/**
	 * Set the String value for this Object that has the attribute name given
	 * @param value The new value that was updated.
	 * @return <tt>true</tt> if the value was added successfully, <tt>false</tt>
	 * otherwise.
	 */
	public boolean setValue(String value){
		if(innerObject==null){
			this.value=value;
			date = new Date().getTime();
			return true;
		}
		return false;
	}
	
	/**
	 * Set an innerObject value for this Object that has the attribute name given
	 * @param attribute The attribute of the Object that was updated.
	 * @param values The new value that was updated.
	 * @return <tt>true</tt> if the value was added successfully, <tt>false</tt>
	 * otherwise.
	 */
	public boolean setValues(String attribute ,UpdateHistory values){
		if(value==null){
			String[] a = attribute.split("/",2);
			if(a.length>1){
				UpdateHistory uh = new UpdateHistory();
				uh.setValues(a[1],values);
				innerObject.put(a[0], uh);
				date = new Date().getTime();
			}else{
				innerObject.put(attribute, values);
				date = new Date().getTime();
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Check if this is a node or a tree.<br>
	 * A Node is where this Object contains only a value and is not an inner object itself.<br>
	 * A Tree is where this Object is an attribute of the parent associated with this Object,
	 * but this Object contains multiple attributes.
	 * @return <tt>true</tt> if this is a Node, <tt>false</tt> otherwise.
	 */
	public boolean hasValue(){
		return innerObject.isEmpty();
	}
	
	/**
	 * Check if the value for the given key is set.
	 * @param key The String key to check if it exists.
	 * @return <tt>true</tt> if a value or null has been associated with the given key,
	 * <tt>false</tt> is returned when the key does not exist.
	 */
	public boolean hasValue(String key){
		return innerObject.containsKey(key);
	}
	
	/**
	 * Get the String value stored in this node.
	 * @return The String value that was stored here, or null if this value was set to null or
	 * this object is a Tree.
	 */
	public String getValue(){
		return value;
	}
	
	/**
	 * Get the UpdateHistory that has been stored at this attribute.
	 * @param attribute The attribute to get the value for.
	 * @return The UpdateHistory Object at this position.
	 */
	public UpdateHistory getValue(String attribute){
		return innerObject.get(attribute);
	}
	
	/**
	 * Get the date at which this attribute was updated.
	 * @return Date representing the date the value at this attribute was update.
	 */
	public Date getDate(){
		return new Date(date);
	}
	
	/**
	 * Merge the update lists assuming they represent the same Object.
	 * @param changed
	 * @return <tt>true</tt> if changes were applied successfully. <tt>false</tt> otherwise.
	 */
	public boolean update(UpdateHistory changed){
		if(hasValue() && getDate().before(changed.getDate())){
			value=changed.getValue();
		}else if(!innerObject.isEmpty()){
			for(String key : changed.innerObject.keySet()){
				if(innerObject.containsKey(key)){
					innerObject.get(key).update(changed.innerObject.get(key));
				}else{
					innerObject.put(key, changed.innerObject.get(key));
				}
			}
		}else{
			return false;
		}
		return true;
	}
	
	/**
	 * Returns an array of values stored in this UpdateHistory.
	 * @return An array of UpdateHistory values.
	 */
	public UpdateHistory[] toArray(){
		return innerObject.values().toArray(new UpdateHistory[]{});
	}
	
	/**
	 * Get the Set of all keys that have been set in this UpdateHistory
	 * @return A Set of String key values.
	 */
	public Set<String> toKeyArray(){
		return innerObject.keySet();
	}
}
